home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / DISK / CSAP312.ARJ / CSAP.C next >
C/C++ Source or Header  |  1992-02-16  |  17KB  |  553 lines

  1. /*
  2.    ****************************  NOTICE!  **************************
  3.    *   Contrary to the current trend  in  MS-DOS  software  this   *
  4.    *   program,  for  whatever  it is worth,  is NOT copyrighted   *
  5.    *   (with the exception of the runtime library  from  Borland   *
  6.    *   International's  Turbo  C)!  The program,  in whole or in   *
  7.    *   part,  may be used freely in any fashion  or  environment   *
  8.    *   desired.  If  you  find this program to be useful to you,   *
  9.    *   do NOT send any contribution to the author;  in the words   *
  10.    *   of  Rick  Conn,   'Enjoy!'  However,   if  you  make  any   *
  11.    *   improvements,  I would enjoy  receiving  a  copy  of  the   *
  12.    *   modified  source.  I  can  be reached,  usually within 24   *
  13.    *   hours,  by  messages  on  any  of  the  Phoenix  systems,   *
  14.    *   particularly:                                               *
  15.    *                                                               *
  16.    *               Technoids Anonymous     [PCBOARD]               *
  17.    *                   (602) 899-4876   300/1200/2400 bps          *
  18.    *                                                               *
  19.    *   or:                                                         *
  20.    *                on CompuServ:           70410,1004             *
  21.    *   or:                                                         *
  22.    *                Don A. Williams                                *
  23.    *                3913 W. Solano Dr. N.                          *
  24.    *                Phoenix, AZ  85019                             *
  25.    *                (602) 841-5333                                 *
  26.    *                                                               *
  27.    *   Every  effort has been made to avoid error and moderately   *
  28.    *   extensive testing has been  performed  on  this  program,   *
  29.    *   however, the author does not warrant it to be fit for any   *
  30.    *   purpose  or  to  be  free  from  error  and disclaims any   *
  31.    *   liability for actual or any other damage arising from the   *
  32.    *   use of this program.                                        *
  33.    *****************************************************************
  34. */
  35.  
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include <dos.h>
  40. #include <ctype.h>
  41. #include <dir.h>
  42. #include <mem.h>
  43. #include <alloc.h>
  44.  
  45. #include "dosstruc.h"
  46.  
  47. /*---   Function Prototypes  ---*/
  48.  
  49. void            Usage (void);
  50. void            GetDPB (int Disk, struct DpbStruct * Dpb);
  51. unsigned        GetDosVersion (void);
  52. char           *strrspn (char *s1, char *s2);
  53. int             AbortProgram (void);
  54.  
  55. void            SortDir (void);
  56.  
  57. /*---   End of Prototypes  ---*/
  58.  
  59. #define MAX12BIT 0x0FF6
  60. #define MAX16BIT 0xFFF6
  61.  
  62. #if defined(__TINY__)
  63.     #define MODEL "Tiny"
  64. #elif defined(__SMALL__)
  65.     #define MODEL "Small"
  66. #elif defined(__COMPACT__)
  67.     #define MODEL "Compact"
  68. #elif defined(__MEDIUM__)
  69.     #define MODEL "Medium"
  70. #elif defined(__LARGE__)
  71.     #define MODEL "Large"
  72. #elif defined(__HUGE__)
  73.     #define MODEL "Huge"
  74. #endif
  75.  
  76. char            Disk;            /* Alpha working disk ('A', 'B', .... )         */
  77. char            CurDir[67];        /* Storage for Current Directory of disk     */
  78. char            Path[67];        /* Storage for Path to sort                     */
  79. char            Parent[67];        /* Storage for Parent part of Path             */
  80. char            Element[13];    /* Storage for Child part of Path             */
  81. char            Line[80];        /* Working storage for strings                 */
  82. char            Order = 'N';    /* Sort key indicator (Default=Name/Ext)     */
  83. char            Inverse = 0;    /* Ascend/Descend indic. (Default=Ascend)     */
  84. char            Level = 0;        /* Recursive sort indic. (default=Recursive) */
  85. char            RSwt = 0;        /* Report switch (Default=No Report)         */
  86. char            VerSwt = 0;        /* Pause for operator verify (Default=off)     */
  87. char            Packed = 1;        /* Elim. "erased" entries (Default=on)         */
  88. char            TruncateSwt = 0;/* Truncate directories (Default=off)     */
  89. unsigned char  *Fat;            /* Pointer to FAT buffer (dynamic)             */
  90. char            FatDirty = 0;    /* FAT needs to be rewritten                 */
  91. int             Is12Bit;        /* 12 / 16 bit cluster indicator             */
  92. int            *CluArray;        /* Cluster Array ptr, dynamically allocated     */
  93. int             Lim, i, j, k, l;
  94. int             OutSectors, OutClusters, BytesPerCluster, ECount;
  95. unsigned        LastCluster;    /* Value for end of cluster chain             */
  96. unsigned        Cluster, Sector, NumSec;
  97. unsigned        FatSize;
  98. unsigned        Version;
  99. long            MinMem;            /* Minimum available memory                     */
  100. unsigned        Freed = 0;        /* Freed cluster count                         */
  101. unsigned        Version;
  102. unsigned        FatSize;
  103. unsigned        DirStart;
  104.  
  105. struct DpbStruct Dpb;            /* Disk Parameter Block (see dosstruc.h)     */
  106. struct ClusterQueue CluQ;        /* Queue of cluster for directory             */
  107. struct DirEntry *DirBuff;        /* Buffer for directory to be sorted         */
  108. struct ExtendedEntry Dir;
  109. struct ClusterEntry *p, *t;
  110. struct ExtFcb   Fcb;
  111.  
  112.  
  113.  void
  114. main (int argc, char *argv[]) {
  115.     char           *strrspn();
  116.     void            SortDir(), Usage();
  117.  
  118.     char           *p, *p1, t1;
  119.     int             i, j;
  120.  
  121.     bdos(0x0D, 0, 0);            /* Reset Disk Subsystem - Flush all buffers */
  122.     fputs("C-Sort And Pack [CSAP]: Version 3.1.2: Date: December 16, 1991", stderr);
  123.     fputs(" [", stderr);
  124.     fputs(MODEL, stderr);
  125.     fputs(" Model]\n", stderr);
  126.     fputs("    use \"CSAP -H\" or \"CSAP ?\" for help.\n\n", stderr);
  127.  
  128.  
  129.     Disk = getdisk() + 'A';
  130.     Line[0] = '\\';
  131.     getcurdir(Disk - '@', &Line[1]);
  132.  
  133.     ctrlbrk(AbortProgram);        /* Install "wrap-up" in Control Break vec. */
  134.  
  135.     /* Interpret command line arguments, if any      */
  136.  
  137.     for (i = 1; i < argc; ++i) {
  138.         if (argv[i][0] == '-') {
  139.             for (j = 1; j < strlen(argv[i]); ++j) {
  140.                 switch (toupper(argv[i][j])) {
  141.                     case 'N':    /* Sort Key = Name/Ext (default)     */
  142.                         Order = 'N';
  143.                         break;
  144.                     case 'D':    /* Sort Key = Date/Time                */
  145.                         Order = 'D';
  146.                         break;
  147.                     case 'E':    /* Sort Key = Ext/Name                 */
  148.                         Order = 'E';
  149.                         break;
  150.                     case 'S':    /* Sort Key = File Size                 */
  151.                         Order = 'S';
  152.                         break;
  153.                     case 'R':    /* Report Dir loc. & "erased"         */
  154.                         RSwt = 1;
  155.                         break;
  156.                     case 'I':    /* Sort order inverse                 */
  157.                         Inverse = 1;
  158.                         break;
  159.                     case 'P':    /* Do NOT remove "erased" entries     */
  160.                         Packed = 0;
  161.                         break;
  162.                     case 'L':    /* Limit sort to one level             */
  163.                         Level = 1;
  164.                         break;
  165.                     case 'V':    /* Request approval before sort         */
  166.                         VerSwt = 1;
  167.                         break;
  168.                     case 'T':    /* Truncate directories                 */
  169.                         TruncateSwt = 1;
  170.                         break;
  171.                     case 'H':
  172.                         Usage();
  173.                     default:    /* Illegal option                     */
  174.                         fprintf(stderr, "Invalid option %s.\n", argv[i]);
  175.                         Usage();
  176.                         break;
  177.                     }
  178.                 }
  179.             }
  180.         else {                    /* Not switch, assume directory name or '?'         */
  181.             if (argv[i][0] == '?') Usage();
  182.             if (argv[i][1] == ':') {    /* Check for disk specified     */
  183.                 Disk = toupper(argv[i][0]);
  184.                 p = &argv[i][2];
  185.                 }
  186.             else p = &argv[i][0];
  187.             if ((p[0] != '\\') && (p[0] != '/')) {
  188.                 Line[0] = '\\';
  189.                 getcurdir(Disk - '@', &Line[1]);
  190.                 p1 = &p[strcspn(p, "\\/")];
  191.                 t1 = *p1;
  192.                 *p1 = '\0';
  193.                 if (!strcmp(p, ".")) {
  194.                     p = p1;
  195.                     *p1 = t1;
  196.                     }
  197.                 else if (!strcmp(p, "..")) {
  198.                     while (!strcmp(p, "..")) {
  199.                         p = strrspn(Line, "\\/");
  200.                         if ((p - Line) == 0) ++p;
  201.                         *p = '\0';
  202.                         if (t1 != '\0') p = ++p1;
  203.                         else p = p1;
  204.                         p1 = &p[strcspn(p, "\\/")];
  205.                         t1 = *p1;
  206.                         *p1 = '\0';
  207.                         }
  208.                     *p1 = t1;
  209.                     }
  210.                 else *p1 = t1;
  211.                 if (*p != '\0') strcat(Line, "\\");
  212.                 strcat(Line, p);
  213.                 }
  214.             else strcpy(Line, p);
  215.             }
  216.         }
  217.  
  218.     /*
  219.      * Get disk information - uses un-documented DOS call, Int 21H, Func. 32H
  220.      * This function has been verified to work correctly in PC/MS-DOS
  221.      * versions 2.0 through 3.3.  It is heavily used by DOS programs such as
  222.      * CHKDSK.
  223.      */
  224.  
  225.     GetDPB(Disk, &Dpb);
  226.     Version = GetDosVersion() & 0xFF;
  227.     switch (Version) {
  228.         case 2:
  229.             FatSize = Dpb.V.V2.FatSize;
  230.             DirStart = Dpb.V.V2.DirStart;
  231.             break;
  232.         case 3:
  233.             FatSize = Dpb.V.V3.FatSize;
  234.             DirStart = Dpb.V.V3.DirStart;
  235.             break;
  236.         case 4:
  237.         case 5:
  238.             FatSize = Dpb.V.V4.FatSize;
  239.             DirStart = Dpb.V.V4.DirStart;
  240.             break;
  241.         default:
  242.             fprintf(stderr, "Invalid DOS version: %d\n", Version);
  243.             exit(1);
  244.         }
  245.  
  246.     /* Establish whether disk has 16-bit or 12-bit clusters  */
  247.  
  248.     if (Dpb.LastCluster > MAX16BIT) {
  249.         fprintf(stderr, "Sorry, CSAP does not yet support FAT entries > 16 bits.\n");
  250.         exit(1);
  251.         }
  252.     Is12Bit = (Dpb.LastCluster > MAX12BIT) ? 0 : 1;
  253.     LastCluster = (Is12Bit) ? 0x0FF8 : 0xFFF8;
  254.  
  255.     /*
  256.      * Get & save current directory of working disk.  We have to change to
  257.      * sort and must restore on termination
  258.      */
  259.  
  260.     CurDir[0] = Disk;
  261.     CurDir[1] = ':';
  262.     CurDir[2] = '\\';
  263.     getcurdir(Disk - '@', (char *) &CurDir[3]);
  264.  
  265.     /* Allocate space to hold entire FAT in memory and read it in  */
  266.  
  267.     if ((Fat = malloc(FatSize * Dpb.SectorSize)) == NULL) {
  268.         fprintf(stderr, "Insufficient memory for FAT.\n");
  269.         exit(1);
  270.         }
  271.     if (absread(Disk - 'A', FatSize, Dpb.FatStart, Fat) != 0) {
  272.         fprintf(stderr, "Error reading FAT.\n");
  273.         exit(1);
  274.         }
  275.  
  276.     /*
  277.      * Develop full path name for directory to be sorted and separate into
  278.      * Parent and Child portions
  279.      */
  280.  
  281.     Path[0] = Parent[0] = Element[0] = '\0';
  282.     Path[0] = Disk;
  283.     Path[1] = ':';
  284.     Path[2] = '\0';
  285.     if ((Line[0] != '\\') && (Line[0] != '/')) {
  286.         strcat(Path, "\\");
  287.         strcpy(&Path[3], &CurDir[3]);
  288.         if ((Path[strlen(Path) - 1] != '\\') && (Path[strlen(Path) - 1] != '/'))
  289.             strcat(Path, "\\");
  290.         }
  291.     strcat(Path, Line);
  292.     p = strrspn(Path, "\\/");
  293.     strcpy(Element, &p[1]);
  294.     if (p[-1] == ':') p++;
  295.     strncpy(Parent, Path, (int) (p - Path));
  296.     Parent[(int) (p - Path)] = '\0';
  297.  
  298.     MinMem = coreleft();        /* Initialize minimum available memory         */
  299.  
  300.     /*
  301.      * Perform sort.  SortDir is recursive and, if Level is not on, will sort
  302.      * sort all levels of the hierarchy from the starting level down
  303.      */
  304.  
  305.     SortDir();
  306.  
  307.     printf("Minimum memory= %ld\n", MinMem);
  308.  
  309.     bdos(0x0D, 0, 0);            /* Reset disk subsystem - flush all buffers */
  310.     if (FatDirty) {
  311.         if (abswrite(Disk - 'A', FatSize, Dpb.FatStart, Fat) != 0) {
  312.             fprintf(stderr, "Error writing FAT.\n");
  313.             exit(1);
  314.             }
  315.         if (Dpb.FatCopies == 2) {
  316.             if (abswrite(Disk - 'A', FatSize, Dpb.FatStart, Fat) != 0) {
  317.                 fprintf(stderr, "Error writing 2nd copy of Fat.\n");
  318.                 exit(1);
  319.                 }
  320.             }
  321.         printf("There %s %d cluster%s (%d bytes) freed\n",
  322.                 (Freed == 1) ? "was" : "were",
  323.                 Freed,
  324.                 (Freed == 1) ? "" : "s",
  325.                 Freed * Dpb.SectorSize * (Dpb.ClusterSize + 1));
  326.         }
  327.     bdos(0x0D, 0, 0);            /* Reset disk subsystem - flush all buffers */
  328.     bdosptr(0x3B, CurDir, 0);    /* Restore input "current" directory     */
  329.     }                                /* end Main */
  330.  
  331.  
  332. /*
  333.  * STRRSPN is simply a reverse version of STRSPN.  It finds the LAST
  334.  * occurance in S1 of any member of S2.  For some reason, none of the C
  335.  * compilers that I use provide this although they all provide STRSPN
  336.  */
  337.  
  338.  char           *
  339. strrspn (char *s1, char *s2) {
  340.     char           *p1;
  341.  
  342.     p1 = s1 + strlen(s1) - 1;
  343.     while (p1 >= s1) {
  344.         if (strchr(s2, *p1) != NULL) return (p1);
  345.         --p1;
  346.         }
  347.     return ((char *) NULL);
  348.     }
  349.  
  350.  
  351. /*
  352.  * SearchFirst --  Search for First Directory Entry. On entry fcb contains an
  353.  * extended File Control Block with file name and attribute bits set.  On
  354.  * exit, fcb contains matched entry unless return code is 255, in which case
  355.  * no match was found.  This routine is used instead of the ones provided by
  356.  * the caller so that the cluster information for the directory can be
  357.  * obtained.
  358.  */
  359.  
  360.  int
  361. SearchFirst (struct ExtFcb * Fcb) {
  362.     union REGS      regs;
  363.  
  364.     regs.x.ax = 0x1100;
  365.     regs.x.dx = (unsigned) Fcb;
  366.     intdos(®s, ®s);
  367.     return ((int) (regs.x.ax & 0xFF));
  368.     }
  369.  
  370.  
  371.  
  372. /*
  373.  * Alu2Sec -- Converts an input cluster number [ALU] into the disk-relative
  374.  * sector for use with DOS Absolute Disk Read [interrupt 25H] or Absolute
  375.  * Disk Write [interrupt 26H].  Requires access to the undocumented DOS Disk
  376.  * Parameter Block [use funtion GetDPB].
  377.  */
  378.  
  379.  long
  380. Alu2Sec (struct DpbStruct * Dpb, unsigned Alu) {
  381.  
  382.     return ((long) (Alu - 2) * (Dpb->ClusterSize + 1) + Dpb->DataStart);
  383.     }
  384.  
  385. /*
  386.  * NextCl -- This function calculates the logical "chaining" of cluster
  387.  * numbers in a File Allocation Table [FAT].  Given an entry cluster number
  388.  * it calculates the next cluster using the array Fat[].
  389.  *
  390.  * If Is12Bit is TRUE then Fat[] is assumed to contain 12 bit entries, otherwise
  391.  * Fat[] is assumed to contain 16 bit entries.
  392.  */
  393.  
  394.  unsigned
  395. NextCl (int Is12Bit, unsigned Cluster, unsigned char Fat[]) {
  396.     unsigned        ClWord, ClOffset;
  397.  
  398.     if (Is12Bit) {                /* 12 bit FAT lookup */
  399.         ClOffset = 3 * Cluster / 2;
  400.         ClWord = Fat[ClOffset] + (Fat[ClOffset + 1] << 8);
  401.         if (Cluster & 1) return (ClWord >> 4);    /* odd cluster  */
  402.         else return (ClWord & 0x0FFF);    /* even cluster */
  403.         }
  404.     else return (((unsigned int *) Fat)[Cluster]);        /* 16 bit FAT lookup */
  405.     }
  406.  
  407.  
  408.  void
  409. FreeCluster (int Is12Bit, unsigned Val, unsigned Cluster, unsigned char Fat[]) {
  410.     extern char     FatDirty;
  411.     extern unsigned    Freed;
  412.     unsigned        ClWord, ClOffset;
  413.  
  414.     if (Is12Bit) {                /* 12 bit FAT lookup */
  415.         ClOffset = 3 * Cluster / 2;
  416.         ClWord = Fat[ClOffset] + (Fat[ClOffset + 1] << 8);
  417.         if (Cluster & 1) ClWord = (ClWord & 0xF) | (Val & 0xFFF0);        /* odd     */
  418.         else ClWord = (ClWord & 0xF000) | (Val & 0x0FFF);
  419.         Fat[ClOffset + 1] = ClWord >> 8;
  420.         Fat[ClOffset] = ClWord & 0xFF;
  421.         }
  422.     else ((unsigned int *) Fat)[Cluster] = Val;    /* 16 bit FAT lookup */
  423.     if (!Val) ++Freed;
  424.     }
  425.  
  426.  
  427. /*
  428.  * PutQueue -- Builds a simple FIFO linked list using dynamically acquired
  429.  * memory.
  430.  */
  431.  
  432.  void
  433. PutQueue (struct ClusterQueue * Q, unsigned Cluster) {
  434.     struct ClusterEntry *p;
  435.  
  436.     if ((p = malloc(sizeof(struct ClusterEntry))) == NULL) {
  437.         fprintf(stderr, "Insufficient memory(1).\n");
  438.         AbortProgram();
  439.         }
  440.     p->Next = NULL;
  441.     p->Cluster = Cluster;
  442.     if (Q->Head == NULL) Q->Head = p;
  443.     else Q->Current->Next = p;
  444.     Q->Current = p;
  445.     Q->Count++;
  446.     }
  447.  
  448.  
  449. /*
  450.  * AbortProgram -- Aborts the program, resetting the current directory, with
  451.  * an error code of 1.
  452.  */
  453.  
  454.  int
  455. AbortProgram (void) {
  456.  
  457.     bdos(0x0D, 0, 0);            /* Reset disk subsystem - flush all buffers */
  458.     bdosptr(0x3B, CurDir, 0);    /* Reset input Current Directory */
  459.     exit(1);
  460.     return (0);
  461.     }
  462.  
  463.  
  464. /*
  465.  * strincmp -- The comparsion routine for the qsort algorithm.
  466.  */
  467.  
  468.  int
  469. strincmp (struct DirEntry * a, struct DirEntry * b) {
  470.     long            t;
  471.  
  472.     /*
  473.      * Ensure that "erased" entries sort high no matter what the sort key is.
  474.      */
  475.  
  476.     if ((a->Name[0] == 0xE5) && (b->Name[0] != 0xE5)) return (1);
  477.     if (b->Name[0] == 0xE5) return (-1);
  478.  
  479.     /*
  480.      * Ensure that directories sort lower than files no matter what sort key
  481.      */
  482.  
  483.     if ((a->Name[0] != 0xE5) && (b->Name[0] != 0xE5)) {
  484.         if ((a->Attribute & 0x10) ^ (b->Attribute & 0x10)) {
  485.             if (a->Attribute & 0x10) return (-1);
  486.             else return (1);
  487.             }
  488.         }
  489.  
  490.     /* Actual sort key compare routines  */
  491.  
  492.     switch (Order) {
  493.         case 'D':                /* Sort key is Date/Time             */
  494.             if (a->ModifyDate < b->ModifyDate) t = -1;
  495.             else if (a->ModifyDate > b->ModifyDate) t = 1;
  496.             else {
  497.                 if (a->ModifyTime < b->ModifyTime) t = -1;
  498.                 else if (a->ModifyTime > b->ModifyTime) t = 1;
  499.                 else t = 0;
  500.                 }
  501.             break;
  502.         case 'N':                /* Sort key is Name/Ext (default)     */
  503.             t = strncmp((char *) a->Name, (char *) b->Name, 11);
  504.             break;
  505.         case 'E':                /* Sort key is Ext/Name                 */
  506.             if ((t = strncmp(a->Ext, b->Ext, 3)) == 0) {
  507.                 t = strncmp((char *) a->Name, (char *) b->Name, 8);
  508.                 }
  509.             break;
  510.         case 'S':                /* Sort key is File Size             */
  511.             t = a->FileSize - b->FileSize;
  512.             break;
  513.         default:
  514.             t = strncmp((char *) a->Name, (char *) b->Name, 11);
  515.             break;
  516.         }
  517.     if (Inverse) t = -t;        /* Sort order is inverse     */
  518.     return ((t < 0) ? -1 : ((t > 0) ? 1 : 0));
  519.     }
  520.  
  521.  unsigned
  522. GetDosVersion (void) {
  523.     union REGS      Regs;
  524.  
  525.     Regs.h.ah = 0x30;
  526.     intdos(&Regs, &Regs);
  527.     return (Regs.x.ax);
  528.     }
  529.  
  530.  
  531.  void
  532. Usage (void) {
  533.  
  534.     printf("USAGE:    CSAP [options] [[d:]directory_name]\n");
  535.     printf("                        or\n");
  536.     printf("          CSAP [[d:]directory_name] [options]\n");
  537.     printf("\n");
  538.     printf("Options:\n");
  539.     printf("    -N    Sort on Name/Ext (default).\n");
  540.     printf("    -D    Sort on Date/Time.\n");
  541.     printf("    -E    Sort on Ext/Name.\n");
  542.     printf("    -S    Sort on File Size.\n");
  543.     printf("\n");
  544.     printf("    -R    Report number of \"erased\" entries and directory location.\n");
  545.     printf("    -I    Inverse sort order, i.e. descending.\n");
  546.     printf("    -P    Do NOT remove \"erased\" entries.\n");
  547.     printf("    -L    Limit sort to a single level.\n");
  548.     printf("    -V    Request confirmation before sorting.\n");
  549.     printf("    -T    Truncate directories.\n");
  550.     printf("    -H    This message.\n");
  551.     exit(0);
  552.     }
  553.